import 'dart:convert';
import 'dart:io';

import 'package:detect/utils/loading_util.dart';
import 'package:detect/utils/toast_util.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

class HttpUtil {
  static final Map<String, HttpUtil> _caches = {};
  late final Dio http;
  String baseUrl;
  LoadingUtil? loading;

  HttpUtil({
    required this.baseUrl,
    Map<String, dynamic>? headers,
    Duration timeout = const Duration(seconds: 10),
    bool showLoading = false,
  }) {
    BaseOptions baseOptions = BaseOptions(
      baseUrl: baseUrl,
      headers: {},
      sendTimeout: timeout.inMilliseconds,
      connectTimeout: timeout.inMilliseconds,
      receiveTimeout: timeout.inMilliseconds,
    );
    baseOptions.headers.addAll(headers ?? {});
    http = Dio(baseOptions);
    http.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        if (showLoading) {
          try {
            LoadingUtil.show(onLoading: (instance) {
              loading = instance;
              handler.next(options);
            });
          } catch (e) {
            handler.next(options);
          }
        } else {
          handler.next(options);
        }
      },
      onResponse: (response, handler) {
        DioError? error;
        if (response.statusCode == 200) {
          if (response.requestOptions.responseType == ResponseType.json) {
            response.data = response.data is String ? jsonDecode(response.data) : response.data;
            if (response.data['code'] == 0) {
              handler.next(response);
            } else {
              error = DioError(requestOptions: response.requestOptions, response: response, type: DioErrorType.response, error: response.data['msg']);
            }
          } else {
            handler.next(response);
          }
        } else {
          error = DioError(requestOptions: response.requestOptions, response: response, type: DioErrorType.response, error: response.statusMessage);
        }
        if (error == null) {
          loading?.close();
        } else {
          handler.reject(error);
        }
      },
      onError: (error, handler) {
        loading?.close();
        handler.next(error);
        ToastUtil.error(error.message);
      },
    ));
    http.interceptors.add(LogInterceptor(request: false, requestBody: true, responseBody: true));
    _caches[baseUrl] = this;
  }

  Future<dynamic> get(String path, [Map<String, dynamic>? query]) async {
    return (await http.get(path, queryParameters: query)).data;
  }

  Future<dynamic> post(String path, [Map<String, dynamic>? params, Map<String, dynamic>? query]) async {
    return (await http.post(path, data: params, queryParameters: query)).data;
  }

  Future<dynamic> delete(String path, Map<String, dynamic> query, [Map<String, dynamic>? params]) async {
    return (await http.delete(path, queryParameters: query, data: params)).data;
  }

  Future<dynamic> put(String path, [Map<String, dynamic>? params, Map<String, dynamic>? query]) async {
    return (await http.put(path, data: params, queryParameters: query)).data;
  }

  Future<dynamic> upload({String path = "upload", required String filePath, String? filename, String key = 'file', Map<String, dynamic>? query}) async {
    filename ??= filePath.split("/").last;
    return (await http.post(path, data: FormData.fromMap({key: await MultipartFile.fromFile(filePath, filename: filename)}), queryParameters: query)).data;
  }

  static Future<String> downloadFile(String url) async {
    Dio dio = Dio();
    Directory directory = await getApplicationDocumentsDirectory();
    String filename = url.split("/").last, filePath = "${directory.path}/$filename";
    dio.interceptors.add(LogInterceptor(request: false, requestBody: true, responseBody: true));
    dio.download(url, filePath);
    return filePath;
  }

  static Future<List<int>> downloadBytes(String url) async {
    Dio dio = Dio(BaseOptions(responseType: ResponseType.bytes));
    dio.interceptors.add(LogInterceptor(request: false, requestBody: true, responseBody: true));
    return (await dio.get<List<int>>(url)).data ?? [];
  }
}
